package cn.xinfei.xdecision.common.model.datax.enums;

import cn.xinfei.xdecision.common.model.datax.exception.XDecisionDataException;
import cn.xinfei.xdecision.common.model.datax.exception.XDecisionDataExceptionType;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <p>配合 {@link ICodeEnum} 使用，所有实现 {@link ICodeEnum}的枚举类 都可以通过该工具类获得枚举</p>
 */
public class EnumUtil {

    private volatile static Map<Class, Map<String, Object>> CODE_MAP = new ConcurrentHashMap<>(1024);
    private volatile static Map<Class, Map<String, Object>> NAME_MAP = new ConcurrentHashMap<>(1024);
    private volatile static Map<Class, Map<String, Object>> ENUM_LIST_MAP = new ConcurrentHashMap<>(1024);


    private static final Logger LOGGER = LoggerFactory.getLogger(EnumUtil.class);

    /**
     * 通过 code获得对应的枚举类，如果找不到 返回null
     *
     * @param enumClass 枚举类
     * @param code      枚举code
     * @param <T>       范型
     * @return
     */
    public static <T extends ICodeEnum> T fromCode(Class<T> enumClass, String code) {
        if (!CODE_MAP.containsKey(enumClass)) {
            synchronized (enumClass) {
                if (!CODE_MAP.containsKey(enumClass)) {
                    Map<String, Object> ENUM_CODE_MAP = new HashMap<>();
                    for (T enumConstant : enumClass.getEnumConstants()) {
                        ENUM_CODE_MAP.put(enumConstant.getCode(), enumConstant);
                    }
                    CODE_MAP.put(enumClass, ENUM_CODE_MAP);
                }
            }
        }
        return (T) CODE_MAP.get(enumClass).get(code);
    }


    /**
     * 通过 code获得对应的枚举类，如果找不到 抛出 XDecisionDataException异常
     *
     * @param enumClass 枚举类
     * @param code      枚举code
     * @param <T>       范型
     * @return
     */
    public static <T extends ICodeEnum> T fromCodeOrThrow(Class<T> enumClass, String code) {
        T result = fromCode(enumClass, code);
        if (null == result) {
            throw new XDecisionDataException(XDecisionDataExceptionType.COMMON_SERVER_ERROR,
                    String.format("cannot find this code %s from enum %s",
                            code,
                            enumClass.getName()));
        }
        return result;
    }


    /**
     * 通过 name获得对应的枚举类，如果找不到 返回null
     *
     * @param enumClass 枚举类
     * @param name      枚举name
     * @param <T>       范型
     * @return
     */
    public static <T extends INameEnum> T fromName(Class<T> enumClass, String name) {
        if (!NAME_MAP.containsKey(enumClass)) {
            synchronized (enumClass) {
                if (!NAME_MAP.containsKey(enumClass)) {
                    Map<String, Object> ENUM_NAME_MAP = new HashMap<>();
                    for (T enumConstant : enumClass.getEnumConstants()) {
                        ENUM_NAME_MAP.put(enumConstant.getName(), enumConstant);
                    }
                    NAME_MAP.put(enumClass, ENUM_NAME_MAP);
                }
            }
        }
        return (T) NAME_MAP.get(enumClass).get(name);
    }


    /**
     * 通过 code获得对应的枚举类，如果找不到 抛出 XDecisionDataException异常
     *
     * @param enumClass 枚举类
     * @param name      枚举name
     * @param <T>       范型
     * @return
     */
    public static <T extends INameEnum> T fromNameOrThrow(Class<T> enumClass, String name) {
        T result = fromName(enumClass, name);
        if (null == result) {
            throw new XDecisionDataException(XDecisionDataExceptionType.COMMON_SERVER_ERROR,
                    String.format("cannot find this name %s from enum %s",
                            name,
                            enumClass.getName()));
        }
        return result;
    }

    public static <T extends ICodeEnum> Map<String, Object> enumToListMap(Class<T> enumClass) {
        if (!ENUM_LIST_MAP.containsKey(enumClass)) {
            synchronized (enumClass) {
                if (!ENUM_LIST_MAP.containsKey(enumClass)) {
                    Map<String, Object> ENUM_CODE_MAP = new HashMap<>();
                    for (T enumConstant : enumClass.getEnumConstants()) {
                        try {
                            String name = enumClass.getMethod("getName").invoke(enumConstant).toString();
                            ENUM_CODE_MAP.put(enumConstant.getCode(), name);
                        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                            e.printStackTrace();
                            LOGGER.error("error in enumToListMap for enumClz = {}", enumClass.getSimpleName(), e);
                        }
                    }
                    ENUM_LIST_MAP.put(enumClass, ENUM_CODE_MAP);
                }
            }
        }
        return ENUM_LIST_MAP.get(enumClass);
    }


}
